package com.atguigu.tingshu.payment.service.impl;

import cn.hutool.core.util.IdUtil;
import com.atguigu.tingshu.common.util.AuthContextHolder;
import com.atguigu.tingshu.model.payment.PaymentInfo;
import com.atguigu.tingshu.payment.config.WxPayV3Config;
import com.atguigu.tingshu.payment.service.PaymentInfoService;
import com.atguigu.tingshu.payment.service.WxPayService;
import com.atguigu.tingshu.payment.util.PayUtil;
import com.atguigu.tingshu.vo.payment.vo.WxPrePayParamsVo;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.core.notification.NotificationParser;
import com.wechat.pay.java.core.notification.RequestParam;
import com.wechat.pay.java.service.payments.jsapi.JsapiServiceExtension;
import com.wechat.pay.java.service.payments.jsapi.model.Amount;
import com.wechat.pay.java.service.payments.jsapi.model.Payer;
import com.wechat.pay.java.service.payments.jsapi.model.PrepayRequest;
import com.wechat.pay.java.service.payments.jsapi.model.PrepayWithRequestPaymentResponse;
import com.wechat.pay.java.service.payments.model.Transaction;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.Map;

@Service
@Slf4j
public class WxPayServiceImpl implements WxPayService {

    private final PaymentInfoService paymentInfoService;
    private final WxPayV3Config wxPayV3Config;
    private final RSAAutoCertificateConfig rsaAutoCertificateConfig;
    private final JsapiServiceExtension jsapiService;

    public WxPayServiceImpl(PaymentInfoService paymentInfoService, WxPayV3Config wxPayV3Config, RSAAutoCertificateConfig rsaAutoCertificateConfig, JsapiServiceExtension jsapiService) {
        this.paymentInfoService = paymentInfoService;
        this.wxPayV3Config = wxPayV3Config;
        this.rsaAutoCertificateConfig = rsaAutoCertificateConfig;
        this.jsapiService = jsapiService;
    }


    @Override
    public WxPrePayParamsVo getWxPrePayParams(String paymentType, String orderNo) {
        // 1.保存本地交易信息, 返回交易信息对象
        Long userId = AuthContextHolder.getUserId();
        PaymentInfo paymentInfo = this.paymentInfoService.savePaymentInfo(paymentType, orderNo, userId);

        // 2.调用微信SDK获取小程序支付所需参数
        // 2.1 构建微信预支付参数对象
        WxPrePayParamsVo wxPrePayParamsVo = new WxPrePayParamsVo();
        // 2.3 构建预支付请求对象
        PrepayRequest prepayRequest = new PrepayRequest();
        Amount amount = new Amount();
        // 设置订单总金额，单位为分，类型为Integer，TODO: 开发阶段测试时，设置为1分钱
//        Integer totalAmount = paymentInfo.getAmount().multiply(BigDecimal.valueOf(100)).intValue();
//        amount.setTotal(totalAmount);
        amount.setTotal(1);
        prepayRequest.setAmount(amount);
        prepayRequest.setAppid(wxPayV3Config.getAppid());
        prepayRequest.setMchid(wxPayV3Config.getMerchantId());
        prepayRequest.setDescription(paymentInfo.getContent());
        prepayRequest.setNotifyUrl(wxPayV3Config.getNotifyUrl());
        prepayRequest.setOutTradeNo(paymentInfo.getOrderNo());
        Payer payer = new Payer();
        payer.setOpenid("odo3j4qp-wC3HVq9Z_D9C0cOr0Zs");
        prepayRequest.setPayer(payer);

        // 2.4 调用微信接口，获取小程序预支付参数
        PrepayWithRequestPaymentResponse response = this.jsapiService.prepayWithRequestPayment(prepayRequest);
        if (response != null) {
            wxPrePayParamsVo.setTimeStamp(response.getTimeStamp());
            wxPrePayParamsVo.setPackageVal(response.getPackageVal());
            wxPrePayParamsVo.setPaySign(response.getPaySign());
            wxPrePayParamsVo.setSignType(response.getSignType());
            wxPrePayParamsVo.setNonceStr(response.getNonceStr());
        }
        return wxPrePayParamsVo;
    }

    @Override
    public Boolean queryPayStatus(String outTradeNo) {
//        // 1.创建查询交易请求对象
//        QueryOrderByOutTradeNoRequest queryOrderByOutTradeNoRequest = new QueryOrderByOutTradeNoRequest();
//        queryOrderByOutTradeNoRequest.setOutTradeNo(outTradeNo);
//
//        // 2.调用微信查询交易状态接口
//        Transaction transaction = this.jsapiService.queryOrderByOutTradeNo(queryOrderByOutTradeNoRequest);
//        if (transaction == null) {
//            return false;
//        }
//        // 3.解析响应结果，返回交易状态
//        Transaction.TradeStateEnum tradeState = transaction.getTradeState();
//        return Transaction.TradeStateEnum.SUCCESS.equals(tradeState);
        // TODO：开发测试过程中，伪造交易成功结果，返回true
        //1.伪造微信交易对象
        Transaction transaction = new Transaction();
        transaction.setOutTradeNo(outTradeNo);
        transaction.setTransactionId("WX" + IdUtil.getSnowflakeNextId());
        //修改支付记录状态
        paymentInfoService.updatePaymentInfoSuccess(transaction);
        return true;
    }

    @Override
    public Map<String, String> paySuccessNotify(HttpServletRequest request) {
        // 1.从请求头中获取微信提交参数
        String wechatPaySerial = request.getHeader("Wechatpay-Serial");  //签名
        String nonce = request.getHeader("Wechatpay-Nonce");  //签名中的随机数
        String timestamp = request.getHeader("Wechatpay-Timestamp"); //时间戳
        String signature = request.getHeader("Wechatpay-Signature"); //签名类型

        //2.构建RequestParam请求参数对象
        // HTTP 请求体 body。切记使用原始报文，不要用 JSON 对象序列化后的字符串，避免验签的 body 和原文不一致。
        String requestBody = PayUtil.readData(request);
        RequestParam requestParam = new RequestParam.Builder()
                .serialNumber(wechatPaySerial)
                .nonce(nonce)
                .signature(signature)
                .timestamp(timestamp)
                .body(requestBody)
                .build();
        // 3.初始化 NotificationParser 解析器对象
        NotificationParser parser = new NotificationParser(rsaAutoCertificateConfig);
        // 4. 调用解析器对象解析方法 验签、解密 并转换成 Transaction
        Transaction transaction = parser.parse(requestParam, Transaction.class);
        Map<String, String> resultMap = new HashMap<>(Map.of("code", "500", "message", "失败"));
        if (transaction == null) {
            return resultMap;
        }
        //4.1 业务验证，验证付款状态以及用户实际付款金额跟商户侧金额是否一致
        if (Transaction.TradeStateEnum.SUCCESS == transaction.getTradeState()) {
            Integer payerTotal = transaction.getAmount().getPayerTotal();
            // todo 调试阶段支付金额为1分，后续改为动态从本地交易记录中获取实际金额
//  PaymentInfo paymentInfo = this.paymentInfoService.getOne(new LambdaQueryWrapper<PaymentInfo>()
//          .eq(PaymentInfo::getOrderNo, transaction.getOutTradeNo()));
//  if(paymentInfo.getAmount().intValue() == payerTotal)
            if (payerTotal == 1) {
                //4.2 更新本地交易记录状态
                paymentInfoService.updatePaymentInfoSuccess(transaction);
                resultMap.put("code", "200");
                resultMap.put("message", "SUCCESS");
            }
        }
        return resultMap;
    }
}
